package ua.epam.muzalevskiy.traction.route;

import static java.lang.Math.atan2;
import static java.lang.Math.cos;
import static java.lang.Math.sin;
import static java.lang.Math.sqrt;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import ua.epam.muzalevskiy.traction.vehicle.Vehicle;
import ua.epam.muzalevskiy.traction.vehicle.VehicleType;

/**
 * Current class describe route entity.
 *
 * @author Muzalevskiy Vitaliy
 */
public class Route {
    
    private static final double EARTH_RADIUS = 6371;
    /** Time what vehicle spend at each stop.*/
    private static final double STOP_TIME = 2;
    /** Reserve time for one side route.*/
    private static final double RESERVE_TIME = 20;
    /** ID of route, mast be unique.*/
    private int id;
    /**Route number.*/
    private String number;
    /**<code>VehicleType</code> value that describe type of vehicle*/
    private VehicleType vehicleType;
    /** Cost of a route.*/
    private double cost;
    /** Speed with which vehicle need to ride for keeping to the schedule.*/
    private double avarageSpeed;
    /** List of stops that vehicle ride thru.*/
    private List<Stop> stops = new LinkedList<Stop>();
    /** List of vehicles in current route.*/
    private List<Vehicle> transportList = new ArrayList<Vehicle>();

    public Route(int id, String number, VehicleType vehicleType, double cost, 
            double avarageSpeed) {
        this.id = id;
        this.number = number;
        this.vehicleType = vehicleType;
        this.cost = cost;
        this.avarageSpeed = avarageSpeed;
    }        

    /**
     * @return the id
     */
    public int getID() {
        return id;
    }
    
    /**
     * @return the number
     */
    public String getNumber() {
        return number;
    }

    /**
     * @return the vehicleType
     */
    public VehicleType getVehicleType() {
        return vehicleType;
    }

    /**
     * @return the cost
     */
    public double getCost() {
        return cost;
    }

    /**
     * @return the avarageSpeed
     */
    public double getAvarageSpeed() {
        return avarageSpeed;
    }

    /**
     * @return the stops
     */
    public List<Stop> getStops() {
        return stops;
    }

    /**
     * @return the transportList
     */
    public List<Vehicle> getTransportList() {
        return transportList;
    }
    
    public void addStop(Stop stop, int number){
        stops.add(number, stop);
    }
    
    public void addVehicle(Vehicle vehicle){
        transportList.add(vehicle);
    }

    
    @Override
    public String toString() {
        return "Route{" + "number=" + number + ", vehicleType=" + vehicleType 
                + ", cost=" + cost + ", avarageSpeed=" + avarageSpeed 
                + ", stops=" + stops + ", transportList=" + transportList + "}\n";
    }

    /**
     * @param stops the stops to set
     */
    public void setStops(List<Stop> stops) {
        this.stops = stops;
    }

    /**
     * @param transportList the transportList to set
     */
    public void setTransportList(List<Vehicle> transportList) {
        this.transportList = transportList;
    }
    
    /**
     * Method to transfer value in degrees to radiance value.
     * 
     * @param degree - value in degrees.
     * @return - value in radiance.
     */
    private static double deg2rad(final double degree) {
        return degree * (Math.PI / 180);
    }

    /**
     * Method find distance between two stops.
     * 
     * @param first - value of first stop
     * @param second - value of second stop
     * @return distance between entered stops.
     */
    private double getDistance(Stop first, Stop second) {
        double distance;
        final double longitude = deg2rad(first.getLongitude() - second.getLongitude());
        final double latitude = deg2rad(first.getLatitude() - second.getLatitude());
        final double intermediateValue = sin(latitude / 2) * sin(latitude / 2)
                + cos(deg2rad(second.getLongitude())) * cos(deg2rad(first.getLatitude()))
                * sin(longitude / 2) * sin(longitude / 2);
        distance = 2 * atan2(sqrt(intermediateValue), sqrt(1 - intermediateValue))
                * EARTH_RADIUS;
        return distance;
    }

    /**
     * Current method find value of average speed, with what vehicle need to ride.
     * 
     * @return average speed value.
     */
    public double getAvarageIntervalTime() {
        int routeIntervalTime;
        double routeTime;
        double distance = 0;
        double speed = this.getAvarageSpeed();
        int vehicleCount = this.getTransportList().size();
        List<Stop> stopList = new ArrayList<Stop>();
        stopList.addAll(this.getStops());
        for (int i = 1; i < stopList.size(); i++) {
            distance += getDistance(stopList.get(i), stopList.get(i - 1));
        }
        routeTime = (distance / (speed / 60)) + STOP_TIME * stopList.size() + RESERVE_TIME;
        routeIntervalTime = (int) Math.round(routeTime/(vehicleCount/2));
        return routeIntervalTime;
    }
}
